package com.glodon.paas.document.service;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.glodon.paas.document.DocumentBaseIT;
import com.glodon.paas.document.api.bean.File;
import com.glodon.paas.document.api.bean.Revision;
import com.glodon.paas.exception.ExistException;
import com.glodon.paas.exception.NotFoundException;
import com.glodon.paas.exception.PrivilegeException;
import com.glodon.paas.exception.StorageException;
import com.glodon.paas.exception.i18n.ErrorCode;
import com.glodon.paas.storage.BulkStorage;
import com.glodon.paas.storage.InitResult;
import com.glodon.paas.storage.Part;
import com.glodon.paas.storage.ReadResult;
import com.glodon.paas.storage.WriteResult;
import com.glodon.paas.test.util.MockBulkStorage;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = { "classpath:test-document-service.xml", "classpath:paas-document-dao-mysql.xml", })
public class FileServiceMySqlIT extends DocumentBaseIT {

    private String          folderId = "";

    private String          fileId1  = "";
    private String          fileId2  = "";
    private String          fileId3  = "";
    private String          fileId4  = "";
    private String          fileId5  = "";
    private String          fileId6  = "";
    private String          fileId7  = "";
    private String          fileId8  = "";
    private String          fileId9  = "";
    private String          fileId10 = "";
    private String          fileId13 = "";
    private String          fileId14 = "";
    private String          fileId15 = "";
    private String          fileId16 = "";

    private int             fileSize = 0;

    @Autowired
    private FileService     service;

    @Autowired
    private RecyclerService recyclerService;

    // true：mock 存储平台，false：使用 file system 存储平台 做测试
    // private boolean useMockStorage = false;

    public void setup() {
        if (!initedData) {
            super.setup();
            initedData = true;
        }
        BulkStorage storage = Mockito.mock(MockBulkStorage.class);
        service.setBulkStorage(storage);
        recyclerService.getRevisionBiz().setBulkStorage(storage);
        InitResult ii = new InitResult();
        ii.setContentType("testContentType");
        ii.setKey("testInitKey");
        ii.setManifest("testManifest");
        ii.setUploadId("testuploadId");

        WriteResult wr = new WriteResult();
        wr.setETag("testEtag");
        wr.setKey("testKey");

        byte[] bytes = "Data of Sample File".getBytes();
        InputStream is = new ByteArrayInputStream(bytes);
        ReadResult rr = new ReadResult(null);
        rr.setName("test.txt");
        rr.setLength(bytes.length);
        rr.setInputStream(is);

        Mockito.when(storage.write(Mockito.anyString(), (InputStream) Mockito.anyObject(), Mockito.anyLong())).thenReturn(wr);
        Mockito.when(storage.read(Mockito.anyString())).thenReturn(rr);

        Mockito.when(storage.isMultiPartUploadSupported()).thenReturn(true);
        Mockito.when(storage.initMultiPartUpload(Mockito.anyString())).thenReturn(ii);
        Mockito.when(storage.uploadPart((InitResult) Mockito.anyObject(), (Part) Mockito.anyObject(),
                                        (InputStream) Mockito.anyObject())).thenReturn(wr);
        Mockito.when(storage.completeMultiPartUpload((InitResult) Mockito.anyObject(), Mockito.anyList())).thenReturn(wr);
    }

    /*
     * id:fileIdPre+Num1 folder12 folder1-folder13 folder1-file1.txt4 folder1-file2.txt has two revisions5 folder2 in
     * recycler6 folder2-folder17 folder2-file1.txt9 folder3 deleted10 folder3-folder113 file1.txt14 file2.txt has two
     * revisions15 file3.txt size more than 5M16 file4.txt have two revisions, one is uploading revision.
     */
    private void initData() throws IOException {
        testGenerateFolderName();
        cleanFile();
        // create folder
        testCreateFolder();
        // upload folder2-file1.txt
        byte[] bytes = "Data of Sample File".getBytes();
        InputStream is = new ByteArrayInputStream(bytes);
        service.uploadFile(ownerId, fileId5, "folder2-file1.txt", 0, bytes.length, is);
        fileId7 = service.getFileId(ownerId, "folder2/folder2-file1.txt");
    }

    private void cleanData() {
        service.remove(ownerId, fileId1);
        service.remove(ownerId, fileId13);
        service.remove(ownerId, fileId14);
        service.remove(ownerId, fileId15);
        service.remove(ownerId, fileId16);
        recyclerService.clean(ownerId);
        service.delExpiredFiles(ownerId, 0, 1000);
        super.cleanFile();
    }

    @Test
    public void testFileService() throws IOException {
        if (service.getBulkStorage() instanceof MockBulkStorage) {
            // useMockStorage = true;
            setup();
        }
        initData();
        testRemove();
        testUploadFile();
        fileId3 = service.getFileId(ownerId, "folder1/folder1-file1.txt");
        fileId4 = service.getFileId(ownerId, "folder1/folder1-file2.txt");

        fileId13 = service.getFileId(ownerId, "file1.txt");
        fileId14 = service.getFileId(ownerId, "file2.txt");
        fileId15 = service.getFileId(ownerId, "file3.txt");
        fileId16 = service.getFileId(ownerId, "file4.txt");

        testGetFileId();
        testGetFile();
        testGetFiles();
        testGetNavigation();
        testGetRevisions();
        testGetUploadedBytes();
        testGetUploadingRevision();
        testGetUsedSpace();
        testDownloadFile();
        testDownloadFiles();
        testDownloadFolder();
        testLockFile();
        testRename();
        testMove();
        testRemoveUploadingRevision();
        testDelExpiredFiles();
        cleanData();
    }

    private void testGetFileId() {
        try {
            service.getFileId(userId, "folder1");
        } catch (NotFoundException ne) {
            assertTrue(ne.getErrorCode() == ErrorCode.FILE_NOT_EXIST);
        }
        try {
            assertNull(service.getFileId(ownerId, "folder2"));
        } catch (NotFoundException ne) {
            assertTrue(ne.getErrorCode() == ErrorCode.FILE_NOT_EXIST);
        }
        try {
            assertNull(service.getFileId(ownerId, "folder3"));
        } catch (NotFoundException ne) {
            assertTrue(ne.getErrorCode() == ErrorCode.FILE_NOT_EXIST);
        }
        try {
            assertNull(service.getFileId(ownerId, "folder3/folder3-folder1"));
        } catch (NotFoundException ne) {
            assertTrue(ne.getErrorCode() == ErrorCode.FILE_NOT_EXIST);
        }

        assertEquals(service.getFileId(ownerId, "folder1"), fileId1);
        assertEquals(service.getFileId(ownerId, "file1.txt"), fileId13);

        assertEquals(service.getFileId(ownerId, "folder1/folder1-folder1"), fileId2);
        assertEquals(service.getFileId(ownerId, "folder1/folder1-file1.txt"), fileId3);
    }

    private void testGetFile() {
        try {
            service.getFile(userId, fileId1, false);
        } catch (PrivilegeException e) {
            assertTrue(e.getErrorCode() == ErrorCode.PRIVILEGE_ERROR);
        }
        try {
            service.getFile(ownerId, fileId5, false);
        } catch (NotFoundException ne) {
            assertTrue(ne.getErrorCode() == ErrorCode.FILE_NOT_EXIST);
        }
        try {
            service.getFile(ownerId, fileId7, false);
        } catch (NotFoundException ne) {
            assertTrue(ne.getErrorCode() == ErrorCode.FILE_NOT_EXIST);
        }
        assertEquals(service.getFile(ownerId, fileId1, false).getFullName(), "folder1");

        File file1 = service.getFile(ownerId, fileId13, false);
        assertEquals(file1.getFullName(), "file1.txt");
        assertTrue(file1.getRevisionNumber() == 1);
        assertTrue(file1.getCount() == 1);

        File file2 = service.getFile(ownerId, fileId14, false);
        assertEquals(file2.getFullName(), "file2.txt");
        assertTrue(file2.getRevisionNumber() == 2);
        assertTrue(file2.getCount() == 2);

        File file4 = service.getFile(ownerId, fileId16, false);
        assertEquals(file4.getFullName(), "file4.txt");
        assertEquals(file4.getRevisionNumber().intValue(), 1);
        assertEquals(file4.getCount().intValue(), 1);
    }

    private void testGenerateFolderName() {
        cleanFile();
        // new folder name 新建文件夹
        String folderName = "\u65b0\u5efa\u6587\u4ef6\u5939";
        assertEquals(service.generateFolderName(ownerId, ownerId), folderName);
        service.createFolder(ownerId, ownerId, folderName);
        assertEquals(service.generateFolderName(ownerId, ownerId), folderName + "(1)");
    }

    private void testGetFiles() {
        try {
            service.getFiles(userId, fileId1, null, null, false);
        } catch (PrivilegeException e) {
            assertTrue(e.getErrorCode() == ErrorCode.PRIVILEGE_ERROR);
        }
        try {
            service.getFiles(ownerId, fileId5, null, null, false);
        } catch (NotFoundException e) {
            assertTrue(e.getErrorCode() == ErrorCode.FILE_NOT_EXIST);
        }
        try {
            service.getFiles(ownerId, fileId7, null, null, false);
        } catch (NotFoundException e) {
            assertTrue(e.getErrorCode() == ErrorCode.FILE_NOT_EXIST);
        }
        assertEquals(service.getFiles(ownerId, fileId1, null, null, false).size(), 3);
        // root
        assertEquals(service.getFiles(ownerId, ownerId, null, null, false).size(), 5);
    }

    private void testGetRevisions() {
        try {
            service.getRevisions(userId, fileId16);
        } catch (PrivilegeException e) {
            assertTrue(e.getErrorCode() == ErrorCode.PRIVILEGE_ERROR);
        }
        try {
            service.getRevisions(ownerId, fileId7);
        } catch (NotFoundException e) {
            assertTrue(e.getErrorCode() == ErrorCode.FILE_NOT_EXIST);
        }
        assertTrue(service.getRevisions(ownerId, fileId14).size() == 2);
        List<Revision> rs = service.getRevisions(ownerId, fileId16);
        assertEquals(rs.size(), 2);
        assertTrue(StringUtils.isNotBlank(rs.get(1).getStorageKey()));
        assertTrue(StringUtils.isBlank(rs.get(0).getStorageKey()));
    }

    private void testGetNavigation() {
        try {
            service.getPath(userId, fileId3);
        } catch (PrivilegeException e) {
            assertTrue(e.getErrorCode() == ErrorCode.PRIVILEGE_ERROR);
        }
        try {
            service.getPath(ownerId, fileId7);
        } catch (NotFoundException e) {
            assertTrue(e.getErrorCode() == ErrorCode.FILE_NOT_EXIST);
        }
        assertEquals(service.getPath(ownerId, fileId3).size(), 2);
        assertEquals(service.getPath(ownerId, fileId1).size(), 1);
    }

    private void testGetUsedSpace() {
        assertTrue(service.getUsedSpace(ownerId) > 0);
    }

    private void testCreateFolder() {
        try {
            service.createFolder(userId, ownerId, "testfolder");
        } catch (NotFoundException e) {
            assertTrue(e.getErrorCode() == ErrorCode.FILE_NOT_EXIST);
        }
        assertNotNull(service.createFolder(ownerId, ownerId, "folder3/folder3-folder1"));
        fileId9 = service.getFileId(ownerId, "folder3");
        fileId10 = service.getFileId(ownerId, "folder3/folder3-folder1");
        try {
            service.createFolder(ownerId, ownerId, "folder3");
        } catch (ExistException e) {
            assertTrue(e.getErrorCode() == ErrorCode.EXIST_SAME_FOLDER);
        }
        assertNotNull(service.createFolder(ownerId, ownerId, "folder2"));
        fileId5 = service.getFileId(ownerId, "folder2");
        assertNotNull(service.createFolder(ownerId, fileId5, "folder2-folder1"));
        fileId6 = service.getFileId(ownerId, "folder2/folder2-folder1");
        assertNotNull(service.createFolder(ownerId, ownerId, "folder1/folder1-folder1"));
        fileId1 = service.getFileId(ownerId, "folder1");
        fileId2 = service.getFileId(ownerId, "folder1/folder1-folder1");
    }

    private void testRename() {
        try {
            service.rename(userId, fileId1, "testfolder");
        } catch (PrivilegeException e) {
            assertTrue(e.getErrorCode() == ErrorCode.PRIVILEGE_ERROR);
        }
        try {
            service.rename(ownerId, fileId5, "testfolder");
        } catch (NotFoundException e) {
            assertTrue(e.getErrorCode() == ErrorCode.FILE_NOT_EXIST);
        }

        service.rename(ownerId, fileId1, "testfolder");
        assertEquals(service.getFile(ownerId, fileId1, false).getFullName(), "testfolder");
        service.rename(ownerId, fileId1, "folder2");
        service.rename(ownerId, fileId13, "testfile");
        assertEquals(service.getFile(ownerId, fileId13, false).getFullName(), "testfile.txt");
        service.rename(ownerId, fileId13, "file1");
    }

    private void testMove() {
        try {
            service.moveFile(userId, fileId3, fileId2);
        } catch (PrivilegeException e) {
            assertTrue(e.getErrorCode() == ErrorCode.PRIVILEGE_ERROR);
        }
        assertEquals(service.getFiles(ownerId, fileId2, null, null, false).size(), 0);
        service.moveFile(ownerId, fileId3, fileId2);
        assertEquals(service.getFiles(ownerId, fileId2, null, null, false).size(), 1);
        service.moveFile(ownerId, fileId3, fileId1);
        assertEquals(service.getFiles(ownerId, fileId2, null, null, false).size(), 0);
    }

    private void testRemove() {
        try {
            service.remove(userId, fileId1);
        } catch (PrivilegeException e) {
            assertTrue(e.getErrorCode() == ErrorCode.PRIVILEGE_ERROR);
        }
        recyclerService.remove(ownerId, fileId9);
        try {
            service.remove(ownerId, fileId10);
        } catch (NotFoundException e) {
            assertTrue(e.getErrorCode() == ErrorCode.FILE_NOT_EXIST);
        }
        service.remove(ownerId, fileId5);
        try {
            service.remove(ownerId, fileId7);
        } catch (NotFoundException e) {
            assertTrue(e.getErrorCode() == ErrorCode.FILE_NOT_EXIST);
        }
    }

    private void testRemoveUploadingRevision() {
        try {
            service.removeUploadingRevision(userId, fileId16);
        } catch (PrivilegeException e) {
            assertTrue(e.getErrorCode() == ErrorCode.PRIVILEGE_ERROR);
        }
        service.removeUploadingRevision(ownerId, fileId16);
        assertNull(service.getUploadingRevision(ownerId, ownerId, "file4.txt", fileSize));
    }

    private void testGetUploadingRevision() {
        try {
            service.getUploadingRevision(userId, fileId16, "file4.txt", fileSize);
        } catch (PrivilegeException e) {
            assertTrue(e.getErrorCode() == ErrorCode.PRIVILEGE_ERROR);
        }
        assertNotNull(service.getUploadingRevision(ownerId, ownerId, "file4.txt", fileSize));
    }

    private void testUploadFile() throws IOException {
        byte[] bytes = "Data of Sample File".getBytes();
        InputStream is = new ByteArrayInputStream(bytes);
        try {
            service.uploadFile(userId, fileId1, "folder1-file1.txt", 0, bytes.length, is);
        } catch (PrivilegeException e) {
            is.close();
            assertTrue(e.getErrorCode() == ErrorCode.PRIVILEGE_ERROR);
        }
        // common upload
        is = new ByteArrayInputStream(bytes);
        service.uploadFile(ownerId, ownerId, "folder1/folder1-file1.txt", 0, bytes.length, is);
        is = new ByteArrayInputStream(bytes);
        service.uploadFile(ownerId, fileId1, "folder1-file2.txt", 0, bytes.length, is);
        is = new ByteArrayInputStream(bytes);
        service.uploadFile(ownerId, fileId1, "folder1-file2.txt", 0, bytes.length, is);

        is = new ByteArrayInputStream(bytes);
        service.uploadFile(ownerId, ownerId, "file1.txt", 0, bytes.length, is);
        is = new ByteArrayInputStream(bytes);
        service.uploadFile(ownerId, ownerId, "file2.txt", 0, bytes.length, is);
        is = new ByteArrayInputStream(bytes);
        service.uploadFile(ownerId, ownerId, "file2.txt", 0, bytes.length, is);

        if (service.getBulkStorage() instanceof MockBulkStorage) {
            uploadBigFileMocket();
        } else {
            uploadBigFile();
        }
    }

    private void uploadBigFile() throws IOException {
        // multi upload 大文件
        FileInputStream fis = new FileInputStream(
                                                  new java.io.File("C:\\Users\\Public\\Videos\\Sample Videos\\test.wmv"));
        fileSize = fis.available();
        service.uploadFile(ownerId, ownerId, "file3.txt", 0, fileSize, fis);
        fis.close();

        fis = new FileInputStream(new java.io.File("C:\\Users\\Public\\Videos\\Sample Videos\\test.wmv"));
        service.uploadFile(ownerId, ownerId, "file4.txt", 0, fileSize, fis);
        fis.close();

        try {
            fis = new FileInputStream(new java.io.File("C:\\Users\\Public\\Videos\\Sample Videos\\test.wmv"));
            fis.skip(fileSize / 2);
            service.uploadFile(ownerId, ownerId, "file4.txt", 0, fileSize, fis);
        } catch (Exception ignore) {
        } finally {
            fis.close();
        }
    }

    private void uploadBigFileMocket() throws IOException {
        // multi upload 大文件
        byte[] bytes = "Data of Sample File".getBytes();
        InputStream fis = new ByteArrayInputStream(bytes);
        fileSize = 26246026;
        service.uploadFile(ownerId, ownerId, "file3.txt", 0, fileSize, fis);
        fis.close();

        fis = new ByteArrayInputStream(bytes);
        service.uploadFile(ownerId, ownerId, "file4.txt", 0, fileSize, fis);
        fis.close();

        try {
            fis = new ByteArrayInputStream(bytes);
            Mockito.when(recyclerService.getRevisionBiz().getBulkStorage().uploadPart((InitResult) Mockito.anyObject(),
                                                                                      (Part) Mockito.anyObject(),
                                                                                      (InputStream) Mockito.anyObject())).thenThrow(new StorageException(
                                                                                                                                                         500,
                                                                                                                                                         "test exception",
                                                                                                                                                         null));
            service.uploadFile(ownerId, ownerId, "file4.txt", 0, fileSize, fis);
        } catch (StorageException ignore) {
        } finally {
            fis.close();
        }
    }

    private void testGetUploadedBytes() {
        assertNotNull(service.getUploadedBytes(fileId16) > 0);
    }

    private void testDownloadFile() throws IOException {
        try {
            service.downloadFile(userId, fileId3, -1);
        } catch (PrivilegeException e) {
            assertTrue(e.getErrorCode() == ErrorCode.PRIVILEGE_ERROR);
        }
        try {
            service.downloadFile(ownerId, fileId3, 2);
        } catch (NotFoundException e) {
            assertTrue(e.getErrorCode() == ErrorCode.FILE_NOT_EXIST);
        }
        ReadResult rr = service.downloadFile(ownerId, fileId3, -1);
        assertEquals(rr.getName(), "folder1-file1.txt");
        rr.getInputStream().close();

    }

    private void testDownloadFiles() throws IOException {
        java.io.File osf = new java.io.File(System.getProperty("java.io.tmpdir") + java.io.File.separator + "test.zip");
        osf.deleteOnExit();
        osf.createNewFile();
        FileOutputStream fos = new FileOutputStream(osf);

        String[] ids = new String[2];
        ids[0] = fileId13;
        ids[1] = fileId14;
        service.downloadFiles(ownerId, ids, fos);
        fos.close();
        FileInputStream fis = new FileInputStream(new java.io.File(System.getProperty("java.io.tmpdir")
                                                                   + java.io.File.separator + "test.zip"));
        assertTrue(fis.available() > 0);
        fis.close();
    }

    private void testDownloadFolder() throws IOException {
        java.io.File osf = new java.io.File(System.getProperty("java.io.tmpdir") + java.io.File.separator + "test.zip");
        osf.deleteOnExit();
        osf.createNewFile();
        FileOutputStream fos = new FileOutputStream(osf);
        service.downloadFolder(ownerId, fileId1, fos);
        fos.close();
        FileInputStream fis = new FileInputStream(new java.io.File(System.getProperty("java.io.tmpdir")
                                                                   + java.io.File.separator + "test.zip"));
        assertTrue(fis.available() > 0);
        fis.close();
    }

    private void testDelExpiredFiles() {
        assertTrue(service.delExpiredFiles(ownerId, 0, 2) == 2);
    }

    public void testLockFile() {
        try {
            service.lockFile(userId, fileId13);
        } catch (PrivilegeException e) {
            assertTrue(e.getErrorCode() == ErrorCode.PRIVILEGE_ERROR);
        }
        service.lockFile(ownerId, fileId13);
        File file = service.getFile(ownerId, fileId13, true);
        assertNotNull(file.getLock());
        service.unlockFile(ownerId, fileId13);
        file = service.getFile(ownerId, fileId13, true);
        assertNull(file.getLock());
    }
}
